Programming With QuickTime Sprites

QTWiredSprite.c Sample Code

This appendix includes sample code from QTWiredSprite.c . For information on how to use this sample code in your application, see Chapter 2, "Sprite Media Handler," and Chapter 3, "Wired Sprites."

//////////
//
//  File:       QTWiredSprites.c
//
//  Contains:   QuickTime wired sprites support for QuickTime movies.
//

//  Written by: Sean Allen
//  Revised by: Chris Flick and Tim Monroe
//              Based (heavily!) on the existing MakeActionSpriteMovie.c
//              code written by Sean Allen.
//
//  Copyright:  © 1997-1998 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//   <2>      03/26/98    rtm     made fixes for Windows compiles
//   <1>      03/25/98    rtm     first file; integrated existing code
//                                                  with shell framework
//  
//
//  This sample code creates a wired sprite movie containing one sprite
//  track. The sprite track contains six sprites: two penguins and four
//  buttons.
//  
//  The four buttons are initially invisible. When the mouse enters (or
//  "rolls over") a button, it appears.
//  When the mouse is clicked inside a button, its image changes to its
//  "pressed" image. When the mouse
//  is released, its image changes back to its "unpressed" image. If the
//  mouse is released inside the button,
//  an action is triggered. The buttons perform the actions of go to
//  beginning of movie, step backward,
//  step forward, and go to end of movie.

//  
//  The first penguin shows all of the buttons when the mouse enters it,
//  and hides them when the mouse exits.
//  The first penguin is the only sprite that has properties that are
//  overriden by the override sprite samples.
//  These samples override its matrix (in order to move it) and its image
//  index (in order to make it "waddle").
//  
//  When the mouse is clicked on the second penguin, it changes its image
//  index to its "eyes closed" image.
//  When the mouse is released, it changes back to its normal image. This
//  makes it appear to blink when clicked on.
//  When the mouse is released over the penguin, several actions are
//  triggered. Both penguins' graphics states are
//  toggled between copyMode and blendMode, and the movie's rate is
//  toggled between zero and one.
//  
//  The second penguin moves once per second. This occurs whether the
//  movie's rate is currently zero or one,
//  because it is being triggered by a gated idle event. When the penguin
//  receives the idle event, it changes
//  its matrix using an action which uses min, max, delta, and wraparound
//  options.
//
//  The movie's looping mode is set to palindrome by a frame-loaded
//  action.
//
//  So, our general strategy is as follows (though perhaps not in the
//  order listed):
//
//      (1) Create a new movie file with a single sprite track.
//      (2) Assign the "no controller" movie controller to the movie.
//      (3) Set the sprite track's background color, idle event
//          frequency, and hasActions properties.
//      (4) Convert our PICT resources to animation codec images with
//          transparency.
//      (5) Create a key frame sample containing six sprites and all of
//          their shared images.
//      (6) Assign the sprites their initial property values.
//      (7) Create a frameLoaded event for the key frame.
//      (8) Create some override samples that override the matrix and
//          image index properties of the first penguin sprite.
//

//  NOTES:
//      
//  *** (1) ***
//  There are event types other that mouse related events (for instance,
//  Idle and FrameLoaded).
//  Idle events are independent of the movie's rate, and they can be
//  gated so they are send at most
//  every n ticks. In our sample movie, the second penguin moves when the
//  movie's rate is zero,
//  and moves only once per second because of the value of the sprite
//  track's idleEventFrequencey property.
//      
//  *** (2) ***
//  Multiple actions may be executed in response to a single event. In
//  our sample movie, rolling over
//  the first penguin shows and hides four different buttons.
//      
//  *** (3) ***
//  Actions may target any sprite or track in the movie. In our sample
//  movie, clicking on one penguin
//  changes the graphics mode of the other.
//      
//  *** (4) ***
//  Conditional and looping control structures are supported. In our
// sample movie, the second penguin
//  uses the "case statement" action.
//      
//  *** (5) ***
//  Sprite track variables that have not been set have a default value of
//  zero. (The second penguin's
//  conditional code relies on this.)
//      
//  *** (6) ***
//  Wired sprites were previously known as "action sprites". Don't let
//  the names of some of the utility
//  functions confuse you. We'll try to update the source code as time
//  permits.
//      
//  *** (7) ***
//  Penguins don't fly, but I hear they totally shred halfpipes on
//  snowboards.
//
//////////
// header files
#include "QTWiredSprites.h"


//////////
//
// QTWired_CreateWiredSpritesMovie
// Create a QuickTime movie containing a wired sprites track.
//
//////////

OSErr QTWired_CreateWiredSpritesMovie (void)
{
    short                   myResRefNum = 0;
    Movie                   myMovie = NULL;
    Track                   myTrack;
    Media                   myMedia;
    StandardFileReply       myReply;
    QTAtomContainer         mySample = NULL;
    QTAtomContainer         myActions = NULL;
    QTAtomContainer         myBeginButton, myPrevButton, myNextButton,
                            myEndButton;
    QTAtomContainer         myPenguinOne, myPenguinTwo,
                            myPenguinOneOverride;
    QTAtomContainer         myBeginActionButton, myPrevActionButton,
                            myNextActionButton, myEndActionButton;
    QTAtomContainer         myPenguinOneAction, myPenguinTwoAction;
    RGBColor                myKeyColor;
    Point                   myLocation;
    short                   isVisible, myLayer, myIndex, myResID, i,
                            myDelta;
    Boolean                 hasActions;
    long                    myFlags = createMovieFileDeleteCurFile |
                                        createMovieFileDontCreateResFile;
    OSType                  myType = FOUR_CHAR_CODE('none');
    UInt32                  myFrequency;
    QTAtom                  myEventAtom;
    long                    myLoopingFlags;
    ModifierTrackGraphicsModeRecord     myGraphicsMode;
    OSErr                   myErr = noErr;

    //////////
    //
    // create a new movie file and set its controller type
    //
    //////////

    // ask the user for the name of the new movie file
    StandardPutFile("\pSprite movie file name:", "\pSprite.mov",
                    &myReply);
    if (!myReply.sfGood)
        goto bail;

    // create a movie file for the destination movie
    myErr = CreateMovieFile(&myReply.sfFile, FOUR_CHAR_CODE('TVOD'), 0,
                            myFlags, &myResRefNum, &myMovie);
    if (myErr != noErr)
        goto bail;
    
    // select the "no controller" movie controller
    myType = EndianU32_NtoB(myType);
    SetUserDataItem(GetMovieUserData(myMovie), &myType, sizeof(myType),
                    kUserDataMovieControllerType, 1);
    
    //////////
    //
    // create the sprite track and media
    //
    //////////
    
    myTrack = NewMovieTrack(myMovie, ((long)kSpriteTrackWidth << 16),
                            ((long)kSpriteTrackHeight << 16), kNoVolume);
    myMedia = NewTrackMedia(myTrack, SpriteMediaType, kSpriteMediaTimeScale, NULL, 0);

    //////////
    //
    // create a key frame sample containing six sprites and all of their
    // shared images
    //
    //////////

    // create a new, empty key frame sample
    myErr = QTNewAtomContainer(&mySample);
    if (myErr != noErr)
        goto bail;

    myKeyColor.red = 0xffff;                        // white
    myKeyColor.green = 0xffff;
    myKeyColor.blue = 0xffff;

    // add images to the key frame sample
    AddPICTImageToKeyFrameSample(mySample, kGoToBeginningButtonUp,
                &myKeyColor, kGoToBeginningButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToBeginningButtonDown,
                &myKeyColor, kGoToBeginningButtonDownIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonUp, &myKeyColor,
                kGoToEndButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonDown,
                &myKeyColor, kGoToEndButtonDownIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonUp,
                &myKeyColor, kGoToPrevButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonDown,
                &myKeyColor, kGoToPrevButtonDownIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonUp,
                &myKeyColor, kGoToNextButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonDo
bail:
    if (mySample != NULL)
        QTDisposeAtomContainer(mySample);

    if (myBeginButton != NULL)
        QTDisposeAtomContainer(myBeginButton);  
            
    if (myPrevButton != NULL)
        QTDisposeAtomContainer(myPrevButton);
                
    if (myNextButton != NULL)
        QTDisposeAtomContainer(myNextButton);
                
    if (myEndButton != NULL)
        QTDisposeAtomContainer(myEndButton);        
        
    if (myResRefNum != 0)
        CloseMovieFile(myResRefNum);

    if (myMovie != NULL)
        DisposeMovie(myMovie);
        
    return(myErr);
}


//////////
//
// QTWired_AddPenguinTwoConditionalActions
// Add actions to the second penguin that transform him (her?) into a two
// state button
// that plays or pauses the movie.
//
// We are relying on the fact that a "GetVariable" for a variable ID
// which has never been set
// will return zero. If we needed a different default value, we could
// initialize it using the
// frameLoaded event.
//
// A higher-level description of the logic is:
//
//  On MouseUpInside
//   If (GetVariable(DefaultTrack, 1) = 0)
//   SetMovieRate(1)
//   SetSpriteGraphicsMode(DefaultSprite, { blend, grey } )
//   SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5),
//              { ditherCopy, white } )
//   SetVariable(DefaultTrack, 1, 1)
//   ElseIf (GetVariable(DefaultTrack, 1) = 1)
//   SetMovieRate(0)
//   SetSpriteGraphicsMode(DefaultSprite, { ditherCopy, white })
//   SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5),
//              { blend, grey })
//   SetVariable(DefaultTrack, 1, 0)
//   Endif
//  End
//
//////////

OSErr QTWired_AddPenguinTwoConditionalActions (QTAtomContainer
                                    theContainer, QTAtom theEventAtom)
{
    QTAtom          myNewActionAtom, myNewParamAtom, myConditionalAtom;
    QTAtom          myExpressionAtom, myOperatorAtom, myActionListAtom;
    short           myParamIndex, myConditionIndex, myOperandIndex;
    float           myConstantValue;
    QTAtomID        myVariableID;
    ModifierTrackGraphicsModeRecord     myBlendMode, myCopyMode;
    OSErr                               myErr = noErr;
    
    myBlendMode.graphicsMode = blend;
    myBlendMode.opColor.red = myBlendMode.opColor.green =
                        myBlendMode.opColor.blue = 0x8fff;      // grey

    myCopyMode.graphicsMode = ditherCopy;
    myCopyMode.opColor.red = myCopyMode.opColor.green =
                        myCopyMode.opColor.blue = 0xffff;       // white

    AddActionAtom(theContainer, theEventAtom, kActionCase,
                    &myNewActionAtom);
    
    myParamIndex = 1;
    AddActionParameterAtom(theContainer, myNewActionAtom, myParamIndex,
                            0, NULL, &myNewParamAtom);

    // first condition
    myConditionIndex = 1;
    AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex,
                        &myConditionalAtom);
    AddExpressionContainerAtomType(theContainer, myConditionalAtom,
                        &myExpressionAtom);
    AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo,
                        &myOperatorAtom);

    myOperandIndex = 1;
    myConstantValue = kButtonStateOne;
    AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant,
                        myOperandIndex, NULL, myConstantValue);

    myOperandIndex = 2;
    myVariableID = kPenguinStateVariableID;
    AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex,
                        0, NULL, 0, myVariableID);

    AddActionListAtom(theContainer, myConditionalAtom,
                        &myActionListAtom);
    AddMovieSetRateAction(theContainer, myActionListAtom, 0,
                        Long2Fix(1));
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, 0, NULL, &myBlendMode, NULL);
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID,
        &myCopyMode, NULL);
    AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0,
        kPenguinStateVariableID, kButtonStateTwo, 0, NULL, 0);

                                    
    // second condition
    myConditionIndex = 2;
    AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex,
        &myConditionalAtom);
    AddExpressionContainerAtomType(theContainer, myConditionalAtom,
        &myExpressionAtom);
    AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo,
        &myOperatorAtom);

    myOperandIndex = 1;
    myConstantValue = kButtonStateTwo;
    AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant,
        myOperandIndex, NULL, myConstantValue);

    myOperandIndex = 2;
    myVariableID = kPenguinStateVariableID;
    AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex,
        0, NULL, 0, myVariableID);

    AddActionListAtom(theContainer, myConditionalAtom,
        &myActionListAtom);
    AddMovieSetRateAction(theContainer, myActionListAtom, 0,
        Long2Fix(0));
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, 0, NULL, &myCopyMode, NULL);
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID,
        &myBlendMode, NULL);
    AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0,
        kPenguinStateVariableID, kButtonStateOne, 0, NULL, 0);


bail:
    return(myErr);
}


//////////
//
// QTWired_AddWraparoundMatrixOnIdle
// Add beginning, end, and change matrices to the specified atom
// container.
//
//////////

OSErr QTWired_AddWraparoundMatrixOnIdle (QTAtomContainer theContainer)
{
    MatrixRecord    myMinMatrix, myMaxMatrix, myDeltaMatrix;
    long            myFlags = kActionFlagActionIsDelta |
                                kActionFlagParameterWrapsAround;
    QTAtom          myActionAtom;
    OSErr           myErr = noErr;
    
    myMinMatrix.matrix[0][0] = myMinMatrix.matrix[0][1] =
        myMinMatrix.matrix[0][2] = EndianS32_NtoB(0xffffffff);
    myMinMatrix.matrix[1][0] = myMinMatrix.matrix[1][1] =
        myMinMatrix.matrix[1][2] = EndianS32_NtoB(0xffffffff);
    myMinMatrix.matrix[2][0] = myMinMatrix.matrix[2][1] =
        myMinMatrix.matrix[2][2] = EndianS32_NtoB(0xffffffff);

    myMaxMatrix.matrix[0][0] = myMaxMatrix.matrix[0][1] =
        myMaxMatrix.matrix[0][2] = EndianS32_NtoB(0x7fffffff);
    myMaxMatrix.matrix[1][0] = myMaxMatrix.matrix[1][1] =
        myMaxMatrix.matrix[1][2] = EndianS32_NtoB(0x7fffffff);
    myMaxMatrix.matrix[2][0] = myMaxMatrix.matrix[2][1] =
        myMaxMatrix.matrix[2][2] = EndianS32_NtoB(0x7fffffff);
    
    myMinMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((1 *
        kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
    myMaxMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((3 *
        kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));

    SetIdentityMatrix(&myDeltaMatrix);
    myDeltaMatrix.matrix[2][1] = Long2Fix(1);
    
    // change location
    myErr = AddSpriteSetMatrixAction(theContainer,
            kParentAtomIsContainer, kQTEventIdle, 0, NULL, 0, 0, NULL,
            &myDeltaMatrix, &myActionAtom);
    if (myErr != noErr)
        goto bail;

    myErr = AddActionParameterOptions(theContainer, myActionAtom, 1,
            myFlags, sizeof(myMinMatrix), &myMinMatrix,
            sizeof(myMaxMatrix), &myMaxMatrix);

bail:
    return(myErr);
}
 

© 1998 Apple Computer, Inc.